


#ifndef __NET_H__
#define __NET_H__



#include "qe_def.h"



#define NET_LOGNAME         "net"
#define net_debug(...)      qelog_debug(NET_LOGNAME, __VA_ARGS__)
#define net_info(...)       qelog_info(NET_LOGNAME, __VA_ARGS__)
#define net_notice(...)     qelog_notice(NET_LOGNAME, __VA_ARGS__)
#define net_err(...)        qelog_error(NET_LOGNAME, __VA_ARGS__)



typedef qe_u32 ipaddr_t;


#define IP_DUMP_FMT             "%d.%d.%d.%d"
#define MAC_DUMP_FMT            "%02x:%02x:%02x:%02x:%02x:%02x"
#define IP_DUMP_ARRAY(addr)
#define MAC_DUMP_ARRAY(addr)    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]



typedef struct {
    void *p;
    int size;
}net_zcb;

struct eth_device {
    struct qe_device parent;
    unsigned char enetaddr[6];
    qe_u8 reserved[2];
    int state;
    
    qe_ret (*init) (struct eth_device *);
    qe_ret (*start_xmit) (struct eth_device *, void *packet, int length);
    qe_ret (*recv) (struct eth_device *);
    void	 (*halt) (struct eth_device *);
    qe_ret (*set_macaddr) (struct eth_device *);
    void *priv;

    struct {
        qe_u32 rx_packets;
        qe_u32 tx_packets;
        qe_u32 rx_bytes;
        qe_u32 tx_bytes;
        qe_u32 rx_errors;
        qe_u32 tx_errors;
        qe_u32 rx_dropped;
        qe_u32 tx_dropped;        
    } stats;
};

void eth_initialize(void);
qe_ret eth_wakeup_queue(void);
void eth_receive(void *pkt, int len);
qe_ret eth_register(struct eth_device *eth, const char *name, 
    qe_u32 flag, void *data);
qe_ret eth_send(void *pkt, int len);
qe_ret eth_queue_xmit(void *pkt, int len);
qe_ret eth_write_hwaddr(struct eth_device *dev, qe_u8 hwaddr[6]);


/*
 * Ethernet header
 */
struct ethernet_hdr {
    qe_u8  dst[6];     /* Destination address */
    qe_u8  src[6];     /* Source address */
    qe_u16 protlen;    /* Protocol or length */
}__attribute__((packed));

/* Ethernet header size */
#define ETHER_HDR_SIZE  (sizeof(struct ethernet_hdr))

#define PROT_IP		    0x0800		/* IP protocol */
#define PROT_ARP	    0x0806		/* IP ARP protocol */
#define PROT_RARP	    0x8035		/* IP ARP protocol */
#define PROT_VLAN	    0x8100		/* IEEE 802.1q protocol */

#define IPPROTO_ICMP    1	        /* Internet Control Message Protocol */
#define IPPROTO_UDP	    17	        /* User Datagram Protocol */

/*
 * Internet Protocol (IP) header.
 */
struct ip_hdr {
    qe_u8  hlv;    /* header length and version */
    qe_u8  tos;    /* type of service */
    qe_u16 len;    /* total length */
    qe_u16 id;     /* identification */
    qe_u16 off;    /* fragment offset field */
    qe_u8  ttl;    /* time to live */
    qe_u8  prot;   /* protocol */
    qe_u16 sum;    /* checksum */
    ipaddr_t    src;    /* Source IP address */
    ipaddr_t    dst;    /* Destination IP address */
}__attribute__((packed));

#define IP_OFFS		    0x1fff /* ip offset *= 8 */
#define IP_FLAGS	    0xe000 /* first 3 bits */
#define IP_FLAGS_RES	0x8000 /* reserved */
#define IP_FLAGS_DFRAG	0x4000 /* don't fragments */
#define IP_FLAGS_MFRAG	0x2000 /* more fragments */

#define IP_HDR_SIZE		    (sizeof(struct ip_hdr))

#define IP_UDP_HDR_SIZE		(sizeof(struct ip_udp_hdr))
#define UDP_HDR_SIZE		(IP_UDP_HDR_SIZE - IP_HDR_SIZE)

/*
 *	Internet Protocol (IP) + UDP header.
 */
struct ip_udp_hdr {
	qe_u8		ip_hl_v;	/* header length and version */
	qe_u8		ip_tos;		/* type of service */
	qe_u16		ip_len;		/* total length */
	qe_u16		ip_id;		/* identification */
	qe_u16		ip_off;		/* fragment offset field */
	qe_u8		ip_ttl;		/* time to live */
	qe_u8		ip_p;		/* protocol	*/
	qe_u16		ip_sum;		/* checksum */
	ipaddr_t	    ip_src;		/* Source IP address */
	ipaddr_t	    ip_dst;		/* Destination IP address */
	qe_u16		udp_src;	/* UDP source port */
	qe_u16		udp_dst;	/* UDP destination port	*/
	qe_u16		udp_len;	/* Length of UDP packet	*/
	qe_u16		udp_xsum;	/* Checksum	*/
}__attribute__((packed));

#define IP_UDP_HDR_SIZE		(sizeof(struct ip_udp_hdr))
#define UDP_HDR_SIZE		(IP_UDP_HDR_SIZE - IP_HDR_SIZE)

/*
 * Address Resolution Protocol (ARP) header.
 */
struct arp_hdr {
	qe_u16		ar_hrd;		    /* Format of hardware address	*/
#   define ARP_ETHER	    1		/* Ethernet  hardware address	*/
	qe_u16		ar_pro;		    /* Format of protocol address	*/
	qe_u8		ar_hln;		    /* Length of hardware address	*/
#   define ARP_HLEN	6
	qe_u8		ar_pln;		    /* Length of protocol address	*/
#   define ARP_PLEN	4
	qe_u16		ar_op;		    /* Operation			        */
#   define ARPOP_REQUEST    1		/* Request  to resolve  address	*/
#   define ARPOP_REPLY	    2		/* Response to previous request	*/

#   define RARPOP_REQUEST   3		/* Request  to resolve  address	*/
#   define RARPOP_REPLY	    4		/* Response to previous request */

	/*
	 * The remaining fields are variable in size, according to
	 * the sizes above, and are defined as appropriate for
	 * specific hardware/protocol combinations.
	 */
	qe_u8		ar_data[0];
#define ar_sha		ar_data[0]
#define ar_spa		ar_data[ARP_HLEN]
#define ar_tha		ar_data[ARP_HLEN + ARP_PLEN]
#define ar_tpa		ar_data[ARP_HLEN + ARP_PLEN + ARP_HLEN]
}__attribute__((packed));

#define ARP_HDR_SIZE	(8+20)		/* Size assuming ethernet	*/

/*
 * ICMP stuff (just enough to handle (host) redirect messages)
 */
#define ICMP_ECHO_REPLY		0	/* Echo reply			*/
#define ICMP_NOT_REACH		3	/* Detination unreachable	*/
#define ICMP_REDIRECT		5	/* Redirect (change route)	*/
#define ICMP_ECHO_REQUEST	8	/* Echo request			*/

/* Codes for REDIRECT. */
#define ICMP_REDIR_NET		0	/* Redirect Net			*/
#define ICMP_REDIR_HOST		1	/* Redirect Host		*/

/* Codes for NOT_REACH */
#define ICMP_NOT_REACH_PORT	3	/* Port unreachable		*/

struct icmp_hdr {
	qe_u8		type;
	qe_u8		code;
	qe_u16		checksum;
	union {
		struct {
			qe_u16	id;
			qe_u16	sequence;
		} echo;
		unsigned long	gateway;
		struct {
			qe_u16	__reserved;
			qe_u16	mtu;
		} frag;
		qe_u8 data[0];
	} un;
}__attribute__((packed));

#define ICMP_HDR_SIZE		(sizeof(struct icmp_hdr))
#define IP_ICMP_HDR_SIZE	(IP_HDR_SIZE + ICMP_HDR_SIZE)

#define ARP_TABLE_SIZE      4

struct arp_entry {
    ipaddr_t   ip;
    qe_u8 ether[6];
}__attribute__((packed));

typedef void net_hook_f(struct ethernet_hdr *eh, struct ip_udp_hdr *iph, int len);

typedef enum {
    NET_HOOK_ARP = 0,
    NET_HOOK_ICMP,
    NET_HOOL_LOCAL_UDP,
    NET_HOOK_NOT_LOCAL,
    NET_HOOK_SIZEOF,
}net_hook_e;

/* Network global data struct */
struct net_gd {
    ipaddr_t      ipaddr;
    ipaddr_t      netmask;
    
    qe_u8    hwaddr[6];
    qe_u8    ether_null[6];
    qe_u8    bcastaddr[6];

    qe_u8   *next_tx_packet;

    ipaddr_t      arp_wait_packet_ip;
    ipaddr_t      arp_wait_replay_ip;
    qe_u8   *arp_wait_packet_mac;
    qe_u8   *next_arp_tx_packet;
    qe_u32   arp_wait_tx_packet_size;
    qe_u32   arp_wait_retry;
    int           arp_entry_num;
    struct arp_entry arp_table[ARP_TABLE_SIZE];
    ipaddr_t      ping_ip;

    qe_u32   ip_pkt_id;

    net_hook_f *hook[NET_HOOK_SIZEOF];

    struct {
        unsigned int ping_success:1;
        unsigned int reserved:31;
    }flag;
}__attribute__((packed));

static inline ipaddr_t net_read_ip(void *from)
{
    ipaddr_t ip;
    qe_memcpy((void *)&ip, from, sizeof(ip));
    return ip;
}

static inline void net_copy_ip(void *to, void *from)
{
	qe_memcpy((void *)to, from, sizeof(ipaddr_t));
}

static inline void net_write_ip(void *to, ipaddr_t ip)
{
	qe_memcpy(to, (void *)&ip, sizeof(ip));
}

/* Transmit a packet */
static inline void net_send_packet(qe_u8 *pkt, int len)
{
	(void) eth_send(pkt, len);
}


qe_bool ipaddr_valid(char *ipaddr_str);
qe_bool netmask_valid(char *netmask);
qe_u32 ip4addr_aton(const char *cp);
void macaddr_aton(const char *p, qe_u8 *mac);

struct net_gd *net_get_gd(void);
unsigned int net_cksum(qe_u8 *ptr, int len);
void net_receive(qe_u8 *pkt, int len);
qe_ret net_stack_init(ipaddr_t ip, ipaddr_t netmask, qe_u8 hwaddr[6]);
qe_ret net_udp_send(qe_u8 *buf, ipaddr_t dest, qe_u8 *ether, 
    int dport, int sport, int length);
int net_update_ether(struct ethernet_hdr *et, qe_u8 *addr, qe_u32 prot);
int net_set_ether(qe_u8 *pkt, qe_u8 addr[6], qe_u16 prot);
void net_set_udp_header(qe_u8 *pkt, ipaddr_t dest, int dport, int sport, int len);
void net_set_hook(net_hook_e type, net_hook_f *f);


#endif /* __NET_H__ */
